use loco_rs::prelude::*;
use serde::{Deserialize, Serialize};

pub use super::_entities::role_has_permissions::{self, ActiveModel, Entity, Model};

#[derive(Debug, Deserialize, Serialize)]
pub struct CreateParams {
    pub permission_id: String,
    pub role_id: String,
}

#[derive(Debug, Validate, Deserialize)]
pub struct Validator {
    #[validate(length(min = 1, message = "Permission ID must not be empty."))]
    pub permission_id: String,
    #[validate(length(min = 1, message = "Role ID must not be empty."))]
    pub role_id: String,
}

impl Validatable for ActiveModel {
    fn validator(&self) -> Box<dyn Validate> {
        Box::new(Validator {
            permission_id: self.permission_id.as_ref().to_owned(),
            role_id: self.role_id.as_ref().to_owned(),
        })
    }
}

#[async_trait::async_trait]
impl ActiveModelBehavior for super::_entities::role_has_permissions::ActiveModel {
    async fn before_save<C>(self, _db: &C, _insert: bool) -> Result<Self, DbErr>
    where
        C: ConnectionTrait,
    {
        self.validate()?;
        Ok(self)
    }
}

impl Model {
    /// finds a role permission by the provided permission_id and role_id
    ///
    /// # Errors
    ///
    /// When could not find role permission or DB query error
    pub async fn find_by_ids(
        db: &DatabaseConnection,
        permission_id: &str,
        role_id: &str,
    ) -> ModelResult<Self> {
        let role_permission = role_has_permissions::Entity::find()
            .filter(
                model::query::condition()
                    .eq(role_has_permissions::Column::PermissionId, permission_id)
                    .eq(role_has_permissions::Column::RoleId, role_id)
                    .build(),
            )
            .one(db)
            .await?;
        role_permission.ok_or_else(|| ModelError::EntityNotFound)
    }

    /// finds role permissions by the provided role_id
    ///
    /// # Errors
    ///
    /// When could not find role permissions or DB query error
    pub async fn find_by_role_id(db: &DatabaseConnection, role_id: &str) -> ModelResult<Vec<Self>> {
        let role_permissions = role_has_permissions::Entity::find()
            .filter(
                model::query::condition()
                    .eq(role_has_permissions::Column::RoleId, role_id)
                    .build(),
            )
            .all(db)
            .await?;
        Ok(role_permissions)
    }

    /// Asynchronously creates a role permission and saves it to the database.
    ///
    /// # Errors
    ///
    /// When could not save the role permission into the DB
    pub async fn create(db: &DatabaseConnection, params: &CreateParams) -> ModelResult<Self> {
        let txn = db.begin().await?;

        if role_has_permissions::Entity::find()
            .filter(
                model::query::condition()
                    .eq(
                        role_has_permissions::Column::PermissionId,
                        &params.permission_id,
                    )
                    .eq(role_has_permissions::Column::RoleId, &params.role_id)
                    .build(),
            )
            .one(&txn)
            .await?
            .is_some()
        {
            return Err(ModelError::EntityAlreadyExists {});
        }

        let role_permission = role_has_permissions::ActiveModel {
            permission_id: ActiveValue::set(params.permission_id.to_string()),
            role_id: ActiveValue::set(params.role_id.to_string()),
        }
        .insert(&txn)
        .await?;

        txn.commit().await?;

        Ok(role_permission)
    }
}
